package com.yuanzi.base.aspect;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yuanzi.base.annotation.RedisCache;
import com.yuanzi.base.annotation.RedisCache.ActionType;
import com.yuanzi.base.annotation.RedisCache.CacheType;
import com.yuanzi.base.redis.HashRedisService;
import com.yuanzi.base.redis.RedisUtils;
import com.yuanzi.base.redis.StringRedisService;
import com.yuanzi.base.util.LogUtils;

/**
 * redis缓存aop<br>
 * 加上RedisCache注解的方法将会执行该AOP<br>
 * 注意：默认方法的第一个参数将作为缓存的key<br>
 * 可通过设置keyNum来确定前几个参数作为缓存的key<br>
 * actionType指定添加或删除缓存，默认ADD<br>
 * cacheType设置缓存类型，默认HASH<br>
 * cacheClass为实体缓存时使用，cacheName在自定义缓存时使用<br>
 * 
 * @author chengshx
 * @date 2017年5月16日
 */
@Aspect
@Component
public class RedisCacheAspect {

	/**
	 * RedisCache AOP<br>
	 * 添加或删除缓存
	 * @param proceedingJoinPoint
	 * @param cache
	 * @return
	 */
	@Around("@annotation(cache)")
	public Object cached(ProceedingJoinPoint proceedingJoinPoint, RedisCache cache) throws Throwable {
		Object result = null;
		String cacheName = cache.cacheName();
		Class<?> cacheClass = cache.cacheClas();
		CacheType cacheType = cache.cacheType();
		int keyNum = cache.keyNum();
		// 判断是实体缓存还是自定义缓存
		if(cacheName.equals("") && cacheClass == RedisCache.class) {
			LogUtils.error(this.getClass(), "cacheClass和cacheName必须设置一个", "");
			return null;
			
		}
		String cacheKey = cacheName;
		if(Objects.equals(cacheName, "")) {
			cacheKey = cacheClass.getSimpleName();
		}
		// 拼装缓存的key
		StringBuilder cacheField = new StringBuilder();
		if(keyNum < 1) {
			LogUtils.error(this.getClass(), "keyNum不能小于1", "");
			return null;
		}
		Object[] args = proceedingJoinPoint.getArgs();
		for(int i = 0; i < keyNum; i++) {
			cacheField.append(String.valueOf(args[i]));
			if(i+1 < keyNum){
				cacheField.append(":");
			}
		}
		// 添加还是删除缓存
		if (cache.actionType() == ActionType.ADD) {
			result = addCache(proceedingJoinPoint, cacheKey, cacheField.toString(), cacheType, false);
		} else if(cache.actionType() == ActionType.DEL) {
			result = delCache(proceedingJoinPoint, cacheKey, cacheField.toString(), cacheType);
		}else if(cache.actionType() == ActionType.ADD_GROUP){
			result = addCache(proceedingJoinPoint, cacheKey + RedisUtils.getGroupKeyPrefix(), cacheField.toString(), cacheType, true);
		}
		return result;
	}

	/**
	 * 添加缓存
	 * @author chengshx
	 * @date 2017年5月18日
	 *
	 * @param proceedingJoinPoint
	 * @param cacheKey
	 * @param cacheType
	 * @param expire
	 * @return
	 */
	private Object addCache(ProceedingJoinPoint proceedingJoinPoint, String cacheKey, String cacheField, CacheType cacheType, boolean isAddGroup) throws Throwable {
		Object result = null;
		Object resultStr = null;
		// 根据不同的缓存类型设置缓存
		switch (cacheType) {
		case HASH: // hash类型缓存
			resultStr = HashRedisService.hget(cacheKey, cacheField);
			result = getResult(resultStr, proceedingJoinPoint);
			if (result != null) {
				HashRedisService.hset(cacheKey, cacheField, JSONObject.toJSONString(result));
				if(!isAddGroup){
					HashRedisService.del(cacheKey + RedisUtils.getGroupKeyPrefix());
				}
			}
			break;
		case STRING: // string类型缓存
			resultStr = StringRedisService.get(cacheKey, cacheField);
			result = getResult(resultStr, proceedingJoinPoint);
			if (result != null) {
				StringRedisService.setEx(cacheKey, cacheField, JSONObject.toJSONString(result), null);
				if(!isAddGroup){
					StringRedisService.del(cacheKey + RedisUtils.getGroupKeyPrefix());
				}
			}
			break;
		default:
			break;
		}
		return result;
	}
	
	/**
	 * 获取切面的返回值
	 * @author chengshx
	 * @date 2017年6月24日
	 *
	 * @param resultStr 缓存数据
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable
	 */
	private Object getResult(Object resultStr, ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		Object result = null;
		if (resultStr != null) {
			MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
			Method method = signature.getMethod();
			Class<?> clazz = method.getReturnType();
			if(clazz == List.class) { // List泛型处理
				Type type = method.getGenericReturnType();
				 if (type instanceof ParameterizedType) {
					 Type[] types = ((ParameterizedType) type).getActualTypeArguments();
					 result = JSONArray.parseArray(String.valueOf(resultStr), (Class<?>)types[0]);
				 }
			} else {
				result = JSONObject.parseObject(String.valueOf(resultStr), clazz);
			}
		} else {
			result = proceedingJoinPoint.proceed();
		}
		return result;
	}
	
	/**
	 * 删除缓存
	 * @author chengshx
	 * @date 2017年5月18日
	 *
	 * @param proceedingJoinPoint
	 * @param cacheKey
	 * @param cacheType
	 * @return
	 */
	private Object delCache(ProceedingJoinPoint proceedingJoinPoint, String cacheKey, String cacheField, CacheType cacheType) throws Throwable {
		Object result = null;
		result = proceedingJoinPoint.proceed();
		
		// 根据不同的缓存类型设置缓存
		switch (cacheType) {
		case HASH: // hash类型缓存
			HashRedisService.hdel(cacheKey, cacheField);
			HashRedisService.del(cacheKey+RedisUtils.getGroupKeyPrefix());
			break;
		case STRING: // string类型缓存
			StringRedisService.del(cacheKey, cacheField);
			StringRedisService.del(cacheKey+RedisUtils.getGroupKeyPrefix());
			break;
		default:
			break;
		}
		return result;
	}
		
}
